/**
 *
 * @author zzhijie
 * @date 2021-04-30
 */
import {Message} from "element-ui";
import {Validate} from "js-utils/validate";

const notEmpty = (val) => !(Validate.isEmpty(val) || val === "undefined" || val === "null");
/**
 * 校验规则缓存Map
 * @type {Map<any, any>}
 */
const ruleMap = new Map();
/**
 * 获取属性的校验规则
 * @param proto
 * @param key
 * @returns {null|*}
 */
const getRules = (proto, key) => {
  if (proto === null) return null;
  let targetClass = ruleMap.get(proto.constructor);
  if (targetClass && targetClass[key]) {
    return targetClass[key];
  } else {
    return getRules(Object.getPrototypeOf(proto), key);
  }
};
/**
 * 设置属性校验规则
 * @param target
 * @param key
 * @param rule
 */
const setRules = (target, key, rule) => {
  let {constructor} = target;
  let targetClass = ruleMap.get(constructor);
  if (!targetClass) ruleMap.set(constructor, targetClass = {});
  if (!targetClass[key]) targetClass[key] = [];
  targetClass[key].unshift(rule);
};
/**
 * 执行校验
 * @param obj
 * @returns {boolean}
 */
const execValid = obj => {
  if (obj && obj._ignoreValid) return true;
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      let val = obj[key];
      let rules = getRules(Object.getPrototypeOf(obj), key);
      if (rules) {
        let valid = rules.every(rule => {
          let valid = rule.validator(val);
          let {message} = rule;
          if (!valid && message) {
            Message({
              message: message,
              type: 'warning'
            });
          }
          return valid;
        });
        if (!valid) return false;
      }
      if (typeof val === "object" && val !== null) {
        let valid = execValid(val);
        if (!valid) return false;
      }
    }
  }
  return true;
};
/**
 * 【成员方法装饰器】
 * 校验方法参数的有效性
 * 验证失败将不会执行目标方法
 * @param target
 * @param key
 * @param descriptor
 * @constructor
 */
export const Valid = (target, key, descriptor) => {
  let originalMethod = descriptor.value;
  descriptor.value = function (...args) {
    let valid = execValid(args);
    if (!valid) return Promise.reject();
    return originalMethod(...args);
  }
};
/**
 * 【类装饰器】
 * 忽略校验
 * 跳过目标类所有校验
 * @param target
 * @constructor
 */
export const IgnoreValid = (target) => {
  if (!target instanceof Function) {
    throw new Error("@IgnoreValid只能装饰类");
  }
  Object.defineProperty(target.prototype, "_ignoreValid", {value: true});
};
/**
 * 【成员变量装饰器】
 * 不为空
 * 不等于null|undefined|""|"null"|"undefined"|空对象{}|空数组[]
 * @param message
 * @returns {boolean}
 */
export const NotEmpty = (message = "") => (target, key) => {
  message = message.message || message;
  setRules(target, key, {message, validator: notEmpty});
};
/**
 * 【成员变量装饰器】
 * 是手机号码
 * @param message
 * @returns {Function}
 * @constructor
 */
export const IsPhone = (message = "") => (target, key) => {
  message = message.message || message;
  setRules(target, key, {message, validator: Validate.isPhone});
};
/**
 * 【成员变量装饰器】
 * 长度校验
 * @param rule
 * @returns {Function}
 * @constructor
 */
export const Length = rule => (target, key) => {
  let {max = Number.MAX_SAFE_INTEGER, min = Number.MIN_SAFE_INTEGER, message} = rule || {};
  let validator = val => {
    val += "";
    let {length} = val;
    return length >= min && length <= max;
  };
  setRules(target, key, {validator, message});
};
/**
 * 【成员变量装饰器】
 * 最大值校验
 * @param rule
 * @returns {Function}
 * @constructor
 */
export const Max = rule => (target, key) => {
  let {max = Number.MAX_SAFE_INTEGER, message} = rule || {};
  let validator = val => (+val) <= max;
  setRules(target, key, {validator, message});
};
/**
 * 【成员变量装饰器】
 * 最小值校验
 * @param rule
 * @returns {Function}
 * @constructor
 */
export const Min = rule => (target, key) => {
  let {min = Number.MIN_SAFE_INTEGER, message} = rule || {};
  let validator = val => (+val) >= min;
  setRules(target, key, {validator, message});
};
/**
 * 【成员变量装饰器】
 * 正则校验
 * @param rule
 * @returns {Function}
 * @constructor
 */
export const Pattern = rule => (target, key) => {
  let {regexp, message} = rule || {};
  if (!regexp instanceof RegExp) {
    throw Error("regexp必须是正则表达式");
  }
  let validator = val => regexp.test(val);
  setRules(target, key, {validator, message});
};
/**
 * 【成员变量装饰器】
 * 校验适配器
 * 用于自定义校验规则
 * @param rule
 * @returns {Function}
 * @constructor
 */
export const ValidAdapter = rule => (target, key) => {
  let {validator} = rule || {};
  if (!validator instanceof Function) {
    throw Error("validator必须是个方法");
  }
  setRules(target, key, rule);
};
